package com.gree.elasticsearch.security;

import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.DefaultClaims;
import io.jsonwebtoken.impl.DefaultHeader;
import io.jsonwebtoken.impl.DefaultJwsHeader;
import io.jsonwebtoken.impl.DefaultJwtBuilder;
import org.apache.commons.collections.ListUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.stream.Collectors;

import static io.jsonwebtoken.Jwts.parser;

/**
 * 总有刁民想害朕，请叫我小花狗
 * create by zxw on 2020/5/30
 */
@Component
public class JwtService {
    private JwtBuilder jwtBuilder = new DefaultJwtBuilder();
    private String TYPE = "jwt";
    private String ALG = "HS256";
    private SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
    private String signKey = "$2a$12$FrAVP17yfHmI4d6XDiXcKecMuGQVp0CxmQTFY7ZQJkX8gzNo4kyAq";

    private Logger log = LoggerFactory.getLogger(JwtService.class.getName());

    public String createJwt(Authentication authentication) {
        String username = authentication.getPrincipal().toString();
        Collection<? extends GrantedAuthority> grantedAuthorities = authentication.getAuthorities();
        List<String> authorities = new ArrayList<String>(grantedAuthorities.size());

        grantedAuthorities.stream().forEach(authority -> {
            authorities.add(authority.getAuthority());
        });

        jwtBuilder.setHeaderParam("typ", "jwt");
        jwtBuilder.setHeaderParam("ecrypt", "true");
        jwtBuilder.setHeaderParam("alg", ALG);
        jwtBuilder.setHeaderParam("ctpy","plain");

        Claims claims = new DefaultClaims();
        claims.put("username", username);
        claims.put("authorities", authorities.toString());
        claims.put("username", username);
        claims.setSubject("spring-security");
        jwtBuilder.setClaims(claims);

        LocalDateTime currentTime = LocalDateTime.now();
        LocalDateTime expiration = currentTime.plusMinutes(10);
        Date date = Date.from(expiration.toInstant(ZoneOffset.of("+8")));

        jwtBuilder.setExpiration(date);
        jwtBuilder.setIssuedAt(new Date());
        jwtBuilder.setIssuer("phoenix-elasticsearch");

        jwtBuilder.signWith(signatureAlgorithm, signKey);

        return jwtBuilder.compact();
    }

    public boolean verifyJwt(String token) {
        JwtParser jwtParser = parser();
        try {
            Jws<Claims> claimsJws =jwtParser.setSigningKey(signKey).parseClaimsJws(token);
            logJwsHeader(claimsJws);
        } catch (SignatureException signatureException) {
            log.debug("verify jwt, and signature error.");
            throw signatureException;
        } catch (ExpiredJwtException expiredJwtException) {
            log.debug("verify jwt, and expiredJwtException");
            throw expiredJwtException;
        }

        return Boolean.TRUE;
    }

    public Authentication getAuthentication(String token) {
        JwtParser jwtParser = parser();
        Jws<Claims> claimsJws =jwtParser.setSigningKey(signKey).parseClaimsJws(token);

        Claims claims = claimsJws.getBody();
        String username = Optional.ofNullable(claims.get("username")).orElse("defaultUser").toString();
        String authorities = Optional.ofNullable(claims.get("authorities")).orElse("[default]").toString();
        authorities = authorities.replace("[", "").replace("]", "");
        log.debug("jwt authorities is {}", authorities);
        List<String> authorityList = Arrays.asList(authorities.split(","));

        Collection<? extends GrantedAuthority> grantedAuthorities = authorityList.stream().map(authority -> { return
                new SimpleGrantedAuthority(authority); }).collect(Collectors.toList());

        JwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(grantedAuthorities);

        return jwtAuthenticationToken;
    }

    private void logJwsHeader(Jws<Claims> claimsJws) {
        JwsHeader jwsHeader = claimsJws.getHeader();
        String algorithm = jwsHeader.getAlgorithm();
        String type = jwsHeader.getType();
        String keyId = jwsHeader.getKeyId();
        String compressionAlgorithm = jwsHeader.getCompressionAlgorithm();
        String contentType = jwsHeader.getContentType();
        String ecrypt = jwsHeader.get("ecrypt").toString();
        Class<? extends JwsHeader> jwsHeaderClass = jwsHeader.getClass();
        log.debug("verify jwt. its header information as follows. algorithm is {}, type is {}, encrypt is {}, " +
                        "compressionAlgorithm is {}, keyId is {}, contentType is {}, jwtHeaderClass is {}", algorithm, type,
                ecrypt, keyId, compressionAlgorithm, contentType, jwsHeaderClass);

        String signature = claimsJws.getSignature();
        log.debug("verify jwt, its signature is {}", signature);
    }

}
